﻿using System;
using NLite.Messaging.Internal;
using System.Runtime.Serialization;
using NLite.Internal;

namespace NLite.Messaging
{
    /// <summary>
    /// 
    /// </summary>
    public interface IMessage
    {
        /// <summary>
        /// 
        /// </summary>
        string Name { get; }

        /// <summary>
        /// 
        /// </summary>
        object Data { get; }
        /// <summary>
        /// 
        /// </summary>
        DateTime Timestamp { get; }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TData"></typeparam>
    public interface IMessage<TData> : IMessage
    {
        /// <summary>
        /// 
        /// </summary>
        new TData Data { get; }
    }

    /// <summary>
    /// 
    /// </summary>
    [DataContract]
    public class Message:IMessage
    {
        
        /// <summary>
        /// 
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        object IMessage.Data { get { return _data; } }

        public DateTime Timestamp { get; private set; }

        protected readonly object _data;

        public Message(string name, object data,DateTime timestamp)
        {
            Assumes.NotNullOrEmpty(name);
            Name = name;
            _data = data;
            Timestamp = timestamp;
        }
        public Message(string name, object data):this(name,data,DateTime.Now)
        {
        }



        public Message(string name):this(name,null,DateTime.Now)
        {
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public override string ToString()
        {
            return _data != null ? _data.ToString() :null;
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TData"></typeparam>
    [Serializable]
    [DataContract]
    public class Message<TData> : Message, IMessage<TData>
    {
        /// <summary>
        /// 
        /// </summary>
        public TData Data { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        public Message(string name, TData data) : base(name, data,DateTime.Now) { }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <param name="timestamp"></param>
        public Message(string name, TData data,DateTime timestamp) : base(name, data,timestamp) { }
    }

    /// <summary>
    /// 
    /// </summary>
    public sealed class MessageFactory
    {
        static readonly Type MessageType = typeof(IMessage);

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="TData"></typeparam>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static IMessage Make<TData>(string name, TData data)
        {
            return MessageFactory.MessageType.IsAssignableFrom(data.GetType()) ? (IMessage)data : new Message<TData>(name, data);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static IMessage Make(string name, object data)
        {
            return MessageFactory.MessageType.IsAssignableFrom(data.GetType()) ?(IMessage)data : new Message(name, data);
        }
        
    }
}
